﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace DeviceSDK.LED.NuoWa.JT100
{
    internal class PackParse
    {
        // Fields
        private bool _isUseDesEncrypt;
        private ushort[] s_CRCValue = new ushort[] { 
        0, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 
        0x1081, 0x108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 
        0x2102, 0x308b, 0x210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 
        0x3183, 0x200a, 0x1291, 0x318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 
        0x4204, 0x538d, 0x6116, 0x709f, 0x420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 
        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 
        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 
        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 
        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x840, 0x19c, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 
        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 
        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0xa50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 
        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0xb58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 
        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0xc60, 0x1de9, 0x2f72, 0x3efb, 
        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0xd68, 0x3ff3, 0x2e7a, 
        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0xe70, 0x1ff9, 
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0xf78
     };

        // Methods
        private ushort CaculateCRC(byte[] data, int len)
        {
            ushort num = 0xffff;
            for (int i = 0; i < len; i++)
            {
                num = (ushort)((num >> 8) ^ this.s_CRCValue[(num ^ data[i]) & 0xff]);
            }
            return num;
        }

        private bool CheckCrc(byte[] data, ushort caculateCrc)
        {
            try
            {
                if (data == null)
                {
                    return false;
                }
                int index = data.Length - 2;
                int num2 = (data[index + 1] << 8) + data[index];
                if (num2 != caculateCrc)
                {
                    return false;
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private byte[] EscapeData(byte[] tdata, int beginIndex, int validLength)
        {
            byte[] buffer = new byte[] { 0xee, 10 };
            byte[] buffer2 = new byte[] { 0xee, 12 };
            byte[] buffer3 = new byte[] { 0xee, 14 };
            byte[] buffer4 = null;
            byte[] buffer5 = new byte[1];
            MemoryStream stream = new MemoryStream();
            try
            {
                byte num = 0;
                for (int i = 0; i < tdata.Length; i++)
                {
                    num = tdata[i];
                    if ((i >= beginIndex) && (i < (beginIndex + validLength)))
                    {
                        switch (num)
                        {
                            case 170:
                                {
                                    stream.Write(buffer, 0, buffer.Length);
                                    continue;
                                }
                            case 0xcc:
                                {
                                    stream.Write(buffer2, 0, buffer2.Length);
                                    continue;
                                }
                            case 0xee:
                                {
                                    stream.Write(buffer3, 0, buffer3.Length);
                                    continue;
                                }
                        }
                        buffer5[0] = num;
                        stream.Write(buffer5, 0, buffer5.Length);
                    }
                    else
                    {
                        buffer5[0] = num;
                        stream.Write(buffer5, 0, buffer5.Length);
                    }
                }
                buffer4 = stream.ToArray();
            }
            catch (Exception exception)
            {
                throw exception;
            }
            finally
            {
                stream.Close();
            }
            return buffer4;
        }

        public bool FindPackageEnd(byte[] data, int beginIndex, out int offset)
        {
            offset = 0;
            for (int i = beginIndex; i < data.Length; i++)
            {
                int num2 = 0;
                while (num2 < 1)
                {
                    if (data[i + num2] != 0xcc)
                    {
                        break;
                    }
                    offset = i;
                    return true;
                }
            }
            return false;
        }

        public bool FindPackageHead(byte[] data, int beginIndex, out int offset)
        {
            offset = 0;
            if ((data != null) && (data.Length >= 1))
            {
                for (int i = beginIndex; i < data.Length; i++)
                {
                    int num2 = 0;
                    while (num2 < 1)
                    {
                        if (data[i + num2] != 170)
                        {
                            break;
                        }
                        offset = i;
                        return true;
                    }
                }
            }
            return false;
        }

        private byte[] GetArrayByValue(int value, int arrayLength)
        {
            byte[] buffer = new byte[arrayLength];
            for (int i = 0; i < arrayLength; i++)
            {
                buffer[i] = (byte)(value >> (8 * i));
            }
            return buffer;
        }

        public bool GetPackageData(byte[] data, int beginIndex, out int packageOffset, out byte[] packageData)
        {
            packageOffset = 0;
            packageData = null;
            if (data == null)
            {
                return false;
            }
            if (!this.FindPackageHead(data, beginIndex, out packageOffset))
            {
                return false;
            }
            int offset = 0;
            if (!this.FindPackageEnd(data, packageOffset, out offset))
            {
                return false;
            }
            int length = ((offset + 1) + 2) - packageOffset;
            packageData = new byte[length];
            Array.Copy(data, packageOffset, packageData, 0, length);
            return true;
        }

        public bool IsEncryptPack(CmdType packType)
        {
            switch (packType)
            {
                case CmdType.IdentifyAndConsultSecurity:
                case CmdType.ReplyIdentifyAndConsultSecurity:
                    return false;
            }
            return this._isUseDesEncrypt;
        }

        public byte[] MakePackage(SocketMode dataSource, byte packType, byte[] data)
        {
            byte[] packData;
            byte[] destinationArray = null;
            byte[] desData = data;
            if ((data != null) && (data.Length > 0))
            {
                if ((dataSource == SocketMode.TCP) && this.IsEncryptPack((CmdType)packType))
                {
                    DesEncrypt.Encrypt(data, data.Length, out desData);
                }
                destinationArray = new byte[(4 + desData.Length) + 1];
                Array.Copy(desData, 0, destinationArray, 4, desData.Length);
            }
            else
            {
                destinationArray = new byte[5];
            }
            destinationArray[0] = 170;
            destinationArray[1] = 0xff;
            destinationArray[2] = 0xff;
            destinationArray[3] = packType;
            destinationArray[destinationArray.Length - 1] = 0xcc;
            int beginIndex = 1;
            int validLength = (destinationArray.Length - 1) - 1;
            destinationArray = this.EscapeData(destinationArray, beginIndex, validLength);
            ushort num3 = this.CaculateCRC(destinationArray, destinationArray.Length);
            byte[] arrayByValue = this.GetArrayByValue(num3, 2);
            packData = new byte[destinationArray.Length + arrayByValue.Length];
            Array.Copy(destinationArray, 0, packData, 0, destinationArray.Length);
            Array.Copy(arrayByValue, 0, packData, destinationArray.Length, arrayByValue.Length);
            return packData;
        }

        public bool ParsePackage(byte[] packData, out byte packType, out byte[] data)
        {
            packType = 0;
            data = null;
            int beginIndex = 0;
            int len = packData.Length - 2;
            ushort caculateCrc = this.CaculateCRC(packData, len);
            if (!this.CheckCrc(packData, caculateCrc))
            {
                return false;
            }
            beginIndex = 1;
            len = ((packData.Length - 1) - 1) - 2;
            packData = this.UnescapeData(packData, beginIndex, len);
            beginIndex += 2;
            packType = packData[beginIndex];
            beginIndex++;
            len = ((((packData.Length - 1) - 1) - 2) - 2) - 1;
            if (len > 0)
            {
                data = new byte[len];
                Array.Copy(packData, beginIndex, data, 0, len);
            }
            return true;
        }

        private byte[] UnescapeData(byte[] tdata, int beginIndex, int validLength)
        {
            byte[] buffer = null;
            byte[] buffer2 = new byte[1];
            MemoryStream stream = new MemoryStream();
            try
            {
                byte num = 0;
                for (int i = 0; i < tdata.Length; i++)
                {
                    num = tdata[i];
                    if ((i >= beginIndex) && (i < (validLength + beginIndex)))
                    {
                        switch (num)
                        {
                            case 0xee:
                                {
                                    switch (tdata[i + 1])
                                    {
                                        case 10:
                                            {
                                                i++;
                                                buffer2[0] = 170;
                                                stream.Write(buffer2, 0, buffer2.Length);
                                                continue;
                                            }
                                        case 12:
                                            {
                                                i++;
                                                buffer2[0] = 0xcc;
                                                stream.Write(buffer2, 0, buffer2.Length);
                                                continue;
                                            }
                                        case 14:
                                            {
                                                i++;
                                                buffer2[0] = 0xee;
                                                stream.Write(buffer2, 0, buffer2.Length);
                                                continue;
                                            }
                                    }
                                    buffer2[0] = num;
                                    stream.Write(buffer2, 0, buffer2.Length);
                                    continue;
                                }
                        }
                        buffer2[0] = num;
                        stream.Write(buffer2, 0, buffer2.Length);
                        continue;
                    }
                    buffer2[0] = num;
                    stream.Write(buffer2, 0, buffer2.Length);
                }
                buffer = stream.ToArray();
            }
            catch (Exception exception)
            {
                throw exception;
            }
            finally
            {
                stream.Close();
            }
            return buffer;
        }



        public CmdType DealPackage(SocketMode dataSource, ref byte[] bufferData, ref int validLengrh)
        {
            PackParse _protocolParse = new PackParse();
            byte[] packageData = null;
            int offset = 0;
            int packageOffset = 0;
            byte packType = 0;
            byte[] data = null;
            while (validLengrh > 0)
            {
                if (!_protocolParse.FindPackageHead(bufferData, 0, out offset))
                {
                    Array.Clear(bufferData, 0, validLengrh);
                    validLengrh = 0;
                    return CmdType.None;
                }
                if (!_protocolParse.GetPackageData(bufferData, offset, out packageOffset, out packageData))
                {
                    return CmdType.None;
                }
                int length = packageData.Length;
                int num5 = packageOffset + length;
                Array.Clear(bufferData, 0, num5);
                Array.Copy(bufferData, num5, bufferData, 0, validLengrh - num5);
                Array.Clear(bufferData, validLengrh - num5, (bufferData.Length - validLengrh) + num5);
                validLengrh -= num5;
                if (_protocolParse.ParsePackage(packageData, out packType, out data))
                {
                    //byte[] buffer3 = data;
                    bufferData = data;
                    return (CmdType) packType;

                }
            }
            return CmdType.None;
        }

        // Properties
        public bool IsUseDesEncrypt
        {
            get
            {
                return this._isUseDesEncrypt;
            }
            set
            {
                this._isUseDesEncrypt = value;
            }
        }
    }

 

}
